package notes.java.nio.demo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.junit.Test;

/**
 * 通道：用来传输数据的，转接源节点和目标节点，不存储数据，配合buffer来传输数据
 */
public class N2_TestChannel {

	/** 测试复制文件 (非直接缓冲区)*/
	@Test
	public void testChannel() throws Exception {
		FileInputStream fis = new FileInputStream(new File("c:/moban.xls"));
		FileOutputStream fos = new FileOutputStream(new File("e:/abc.xls"));
		
		// 1、获取通道
		FileChannel inChannel = fis.getChannel();
		FileChannel outChannel = fos.getChannel();
		
		// 2、分配buffer
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		
		// 3、读写
		while(inChannel.read(buffer) != -1) {
			buffer.flip(); // 切换写模式
			outChannel.write(buffer);
			buffer.clear(); // 清空
		}
		
		outChannel.close();
		inChannel.close();
		fos.close();
		fis.close();
	}

	/** 测试复制文件 (直接缓冲区), 速度比上面的快。使用了jdk1.7以上的方式 
	 * 与buffer.allocateDirect()一样，由系统内存开销，占用系统资源，不能及时释放，容易出问题
	 * */
	@Test
	public void testChannel2 () throws Exception{
		
		// 1、获取管道
		FileChannel inChannel = FileChannel.open(Paths.get("c:/moban.xls"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("d:/def.xls"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE_NEW);// 其中CREATE_NEW创建新文件，文件存在报错， 若是CREATE 文件存在覆盖
		
		// 2、分配缓冲区
		MappedByteBuffer inBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
		MappedByteBuffer outBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); // 这里是READ_WRITE 上面的outChannel也需要READ和WRITE，否则报错
		
		// 3、读写，省去循环copy操作
		byte[] bytes = new byte[inBuffer.limit()];
		inBuffer.get(bytes);
		outBuffer.put(bytes);
		
		inChannel.close();
		outChannel.close();
	}
	
	/** 测试通道之间的传输（直接缓冲区）*/
	@Test
	public void testChannelTransfer() throws Exception{
		// 1、获取管道
		FileChannel inChannel = FileChannel.open(Paths.get("c:/moban.xls"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("d:/def.xls"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE_NEW);// 其中CREATE_NEW创建新文件，文件存在报错， 若是CREATE 文件存在覆盖
		
		// 2、更行传输transferTo
		inChannel.transferTo(0, inChannel.size(), outChannel);
		
		// 或者用transferFrom
//		outChannel.transferFrom(inChannel, 0, inChannel.size());
		
		inChannel.close();
		outChannel.close();
	}
	
	/** 分散与聚集
	 * 分散读取：将通道中的数据分散到多个缓冲区中，依次将缓冲区填满
	 * 聚集写入：将多个缓冲区中的数据聚集到通道中，依次写入通道
	 * 
	 * 有点问题：
	 * 1、缓冲区写满了，就不写了，当文件稍大，出问题，不能读取整个文件
	 * 2、读取txt行，其它的好像不行。
	 * */
	@Test
	public void testChannelScatterAndGather() throws Exception {
		// 1、获取读取通道
		RandomAccessFile ras = new RandomAccessFile(new File("d:/save.txt"), "rw");
		FileChannel channel = ras.getChannel();
		
		// 2、分配多个buffer
		ByteBuffer buffer1 = ByteBuffer.allocate(100);
		ByteBuffer buffer2 = ByteBuffer.allocate(1024);
		ByteBuffer[] bufs = {buffer1, buffer2};
		
		// 3、分散读取到缓冲区中
		channel.read(bufs);
		
		// 4、获取写入通道
		RandomAccessFile ras2 = new RandomAccessFile(new File("d:/xxx.txt"), "rw");
		FileChannel channel2 = ras2.getChannel();
		
		// 5、切换模式
		for (ByteBuffer byteBuffer : bufs) {
			byteBuffer.flip();
		}
		
		// 6、聚集写入
		channel2.write(bufs);
		
	}
	
	// 获取支持的字符集数
	@Test
	public void testCharsets(){
		Map<String, Charset> map = Charset.availableCharsets();
		
		Set<Entry<String, Charset>> set = map.entrySet();
		
		for (Entry<String, Charset> entry : set) {
			System.out.println(entry.getKey() + "=" + entry.getValue());
		}
	}
	
	//字符集
	@Test
	public void testCharset() throws IOException{
		Charset cs1 = Charset.forName("GBK");
		
		//获取编码器
		CharsetEncoder ce = cs1.newEncoder();
		
		//获取解码器
		CharsetDecoder cd = cs1.newDecoder();
		
		CharBuffer cBuf = CharBuffer.allocate(1024);
		cBuf.put("中国人万岁！");
		cBuf.flip();
		
		//编码
		ByteBuffer bBuf = ce.encode(cBuf);
		
		for (int i = 0; i < 12; i++) {
			System.out.println(bBuf.get());
		}
		
		//解码
		bBuf.flip();
		CharBuffer cBuf2 = cd.decode(bBuf);
		System.out.println(cBuf2.toString());
		
		System.out.println("------------------------------------------------------");
		
		Charset cs2 = Charset.forName("GBK");
		bBuf.flip();
		CharBuffer cBuf3 = cs2.decode(bBuf);
		System.out.println(cBuf3.toString());
	}
}
